<?php

namespace lico\Runtime;

use lico\Runtime\Request;
use lico\Tool\Exception\RouteNotFoundException;

class Route
{

    protected $appVal;
    protected $appNameSpaceVal;
    protected $controllerNameSpaceVal;
    protected $moduleNameSpaceVal;
    protected $versionNameSpaceVal;
    protected $moduleVal;
    protected $versionVal;
    protected $controllerVal;
    protected $methodVal;

    protected $_request;

    public function __construct(Request $request)
    {
        $this->_request = $request;
    }

    public function run()
    {
    
        $this->init();
        return true;
    }

    /**
     * 初始化
     */
    public function init()
    {

        if (is_null($this->controllerNameSpaceVal)) {
            $routeConfig = constant("SYSCONFIG")['Route'];
            $app = constant("SYSCONFIG")['App']['main'];
            $module = $this->_request->module();
            $this->moduleVal = $module;
            $this->appVal = $app;
 
            $appConfig = $this->appConfig($module);
            if (isset($appConfig['route']['strictMode'])) {
                $strictMode = $appConfig['route']['strictMode'];
            } else {
                $strictMode = $routeConfig['strictMode'];
            }

            if ($strictMode) {//严格模式
                list($version,$controller,$method) = $this->routeParse();
            } else {
                $controller = $this->_request->controller();
                $version = $this->_request->version();
                $method = $this->_request->method();
            }

            $appNameSpace = '\\'.$app;
            $moduleNameSpace = $appNameSpace.'\\'.'controller'.'\\'.$module;
            $versionNameSpace = $moduleNameSpace.'\\'.$version;
            $controllerNameSpace = $versionNameSpace.'\\'.$controller;

            $this->versionVal = $version;
            $this->controllerVal = $controller;
            $this->methodVal = $method;

            $this->appNameSpaceVal = $appNameSpace;
            $this->moduleNameSpaceVal = $moduleNameSpace;
            $this->versionNameSpaceVal = $versionNameSpace;
            $this->controllerNameSpaceVal = $controllerNameSpace;

            $controllerFolder = ROOT_PATH.$app.DS.'controller';
            $controllerModule = $controllerFolder.DS.$this->moduleVal;
            $controllerVersion = $controllerFolder.DS.$this->moduleVal.DS.$this->versionVal;
            $controllerFile = $controllerVersion.DS.$this->controllerVal;
            
            if (!is_dir($controllerModule)) {
                throw new RouteNotFoundException($this->moduleVal." module not exist ", 1);
            }
            if (!is_dir($controllerVersion)) {
                throw new RouteNotFoundException($this->versionVal." version not exist ", 1);
            }
            if (!file_exists($controllerFile.EXT)) {
                throw new RouteNotFoundException($this->controllerVal." controller not exist ", 1);
            }
            
            $controllerClassInit = new $controllerNameSpace($this->_request,\lico\Container::getBowl('lico\Runtime\Response')) ;//必须先引入，否则class_exists函数，当第二参数是false时检查不到
            if (!class_exists($controllerNameSpace, false)) {
                throw new RouteNotFoundException($this->controllerVal." class not define ", 1);
            }
            // P($controllerClassInit,$method);
            if (!method_exists($controllerClassInit, $this->methodVal)) {
                throw new RouteNotFoundException($this->methodVal." method not define ", 1);
            }
        }
    }

    /**
     * 路由解析
     */
    public function routeParse()
    {

        $module = $this->_request->module();
        $controller = $this->_request->controller();
        $version = $this->_request->version();
        $method = $this->_request->method();

        $routeConfig = constant('SYSCONFIG')['Route'];
        if (!file_exists(ROOT_PATH.$routeConfig['main'].DS.$module.EXT)) {
            throw new RouteNotFoundException($module." route not default ", 1);
        }
        $route = include ROOT_PATH.$routeConfig['main'].DS.$module.EXT;
        $combRoute = [];
        foreach ($route as $key => $value) {
            $newkey = ltrim($key, '/');
            if (!isset($value[0])) {
                foreach ($value as $k => $v) {
                    $combRoute[$module.'/'.$newkey.'/'.$k] = $v;
                }
            } else {
                $combRoute[$module.'/'.$newkey] = $value;
            }
        }
        // p($combRoute);
        $clientPath = $this->_request->clientPath();
        $matchSuccess = false;
        $routeValue = [];
        foreach ($combRoute as $key => $value) {
            if ($key == ltrim($clientPath, '/')) {
                $matchSuccess = true;
                $routeValue = $value;
                break;
            }
        }
        // p(ltrim($clientPath,'/'),$routeValue);
        if (!$matchSuccess) {
            throw new RouteNotFoundException($clientPath." route not exist ", 1);
        }

        $allStyle = ['GET','POST','PUT'];
        $style =  $this->_request->style();
        if (!empty($routeValue['method']) && $style != strtoupper($routeValue['method'])) {
            throw new RouteNotFoundException($clientPath." route method not allow ", 1);
        }
        $realRoute = current($routeValue);
        $realRouteArr = explode('/', ltrim($realRoute, '/'));
        if (count($realRouteArr) != 3) {
            throw new RouteNotFoundException($clientPath." route value was error ", 1);
        }

        return $realRouteArr;
    }

    /**
     * 获取指定模块应用配置信息
     *
     * @param [type] $module
     * @return void
     */
    public function appConfig($module)
    {
        $userConfig = constant("SYSCONFIG")['Config']['main'];

        $config = $newConfig = [];
        $configFile = ROOT_PATH . $userConfig . DS . $module . EXT;
        if (file_exists($configFile)) {
            $config = include $configFile;
        }
        foreach ($config as $key => $value) {
            $newConfig[$key] = isset($defaultConfig[$key]) ? array_merge($defaultConfig[$key], $config[$key]) : $config[$key];
        }

        $defaultConfig = include LICO_PATH . 'Const' . DS . 'config' . EXT;
        foreach ($defaultConfig as $key => $value) {
            !isset($config[$key]) && $newConfig[$key] = $defaultConfig[$key];
        }
        // p($config,$defaultConfig,$newConfig);
        return $newConfig;
    }

    /**
     * 模块命名空间
     */
    public function app()
    {
        if (is_null($this->appVal)) {
            $this->init();
        }
        return $this->appVal;
    }

    /**
     * 方法
     */
    public function module()
    {
        if (is_null($this->moduleVal)) {
            $this->init();
        }
        return $this->moduleVal;
    }

    /**
     * 方法
     */
    public function version()
    {
        if (is_null($this->versionVal)) {
            $this->init();
        }
        return $this->versionVal;
    }

    /**
     * 方法
     */
    public function controller()
    {
        if (is_null($this->controllerVal)) {
            $this->init();
        }
        return $this->controllerVal;
    }

    /**
     * 方法
     */
    public function method()
    {
        if (is_null($this->methodVal)) {
            $this->init();
        }
        return $this->methodVal;
    }

    /**
     * 应用命名空间
     */
    public function appNameSpace()
    {
        if (is_null($this->appNameSpaceVal)) {
            $this->init();
        }
        return $this->appNameSpaceVal;
    }

    /**
     * 模块命名空间
     */
    public function moduleNameSpace()
    {
        if (is_null($this->moduleNameSpaceVal)) {
            $this->init();
        }
        return $this->moduleNameSpaceVal;
    }

    /**
     * 版本控制的命名空间
     */
    public function versionNameSpace()
    {
        if (is_null($this->versionNameSpaceVal)) {
            $this->init();
        }
        return $this->versionNameSpaceVal;
    }

    /**
     * 控制器命名空间
     */
    public function controllerNameSpace()
    {
        if (is_null($this->controllerNameSpaceVal)) {
            $this->init();
        }
        return $this->controllerNameSpaceVal;
    }
}
